#include <time.h>
#include <assert.h>

#include "Utility\Convertor.h"
#include "Type\Permission.h"

#include "..\Store\UserTable.h"

#include "UserManager.h"
#include "..\Type\Province.h"
#include "..\Type\Permission.h"
#include "..\Store\PermissionsTable.h"
#include "SignatureManager.h"
#include "..\Store\TempUserTable.h"
#include "Type\UserInfo.h"
#include "Store\UserInfoTable.h"

using namespace std;
using namespace TCServer::Type;

namespace TCServer
{
    namespace Manager
    {
        UserManager::UserManager()
        {
        }
        UserManager::~UserManager()
        {
        }

        UserManager* UserManager::instance()
        {
            static auto _instance = new UserManager;
            return _instance;
        }

        TCServer::Manager::UserManager::LoginResult UserManager::login(const QString &username, const QString &password)
        {
            if (username == "tourist")
            {
                auto userId = -1;
                auto permissions = QSet<Type::Permission>{ Type::Permission::Tourist };
                auto signature = Manager::SignatureManager::instance()->generateFromUserPermissions(userId, permissions);

                return
                    LoginResult{ true,
                    move(signature),
                    move(permissions) };
            }

            auto res = Store::UserTable::id(username, Utility::Convertor::toPasswordHash(password));
            if (get<bool>(res))
            {
                auto userId = get<quint64>(res);
                return login_with_id(userId);
            }
            return LoginResult{ false };
        }

        TCServer::Manager::UserManager::LoginResult UserManager::login_with_id(quint64 userId)
        {
            if (!Store::UserTable::isIdExist(userId))
                return { false };

            auto permissions = Store::PermissionsTable::query(userId);
            auto signature = Manager::SignatureManager::instance()->generateFromUserPermissions(userId, permissions);

            Store::UserTable::flushLoginDatetime(userId);
            return
                LoginResult{ true,
                move(signature),
                move(permissions) };
        }

        bool UserManager::usernameExist(const QString & username)
        {
            return Store::UserTable::isUsernameExist(username);
        }

        bool UserManager::emailExist(const QString &email)
        {
            return Store::UserTable::isEmailExist(email);
        }

        bool UserManager::phoneExist(const QString &phone)
        {
            return Store::UserTable::isPhoneExist(phone);
        }

        void UserManager::registerWithUsername(const QString &username, const QString &password)
        {
            auto user = createDefaultUser();
            user.username = username;
            user.passwordHash = Utility::Convertor::toPasswordHash(password);
            quint64 userId = Store::UserTable::insert(user);
        }

        void UserManager::registerWithEmail(const QString &email, const QString &password)
        {
            auto user = createDefaultUser();
            user.email = email;
            user.passwordHash = Utility::Convertor::toPasswordHash(password);
            quint64 userId = Store::UserTable::insert(user);
        }

        void UserManager::registerWithPhone(const QString &phone, const QString &password)
        {
            auto user = createDefaultUser();
            user.phone = phone;
            user.passwordHash = Utility::Convertor::toPasswordHash(password);
            quint64 userId = Store::UserTable::insert(user);
        }

        Type::User UserManager::queryUser(quint64 userId)
        {
            return Store::UserTable::query(userId);
        }

        bool UserManager::changeUser(const Type::User &newval)
        {
            Store::UserTable::update(newval);
            return true;
        }

        TCServer::Type::UserInfo UserManager::queryInfo(quint64 userId)
        {
            return Store::UserInfoTable::query(userId);
        }

        bool UserManager::changeInfo(const Type::UserInfo &newInfo)
        {
            Store::UserInfoTable::update(newInfo);
            auto userId = newInfo.id;
            if (!Store::PermissionsTable::query(userId).contains(Permission::User))
                Store::PermissionsTable::insert(userId, Permission::User);
            return true;
        }

        Type::User UserManager::createDefaultUser()
        {
            return Type::User{
                0,  // `users`.`id`,

                "", // `users`.`username`,
                "", // `users`.`email`,
                "", // `users`.`phone`,
                "000111", // `users`.`passwordHash`,

                QDateTime::currentDateTime(), // `users`.`create_time`,
            };
        }
    }
}